﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ProDivingJudge2salmon14
{
    public class ClientConnection : IClientConnection
    {
        public int serverPort = 0;
        public string serverIP = "";
        public string localIP = "";
        public bool isConnected = false;

        private DataManager dataManager = new DataManager();
        private int serverIPNumber = 0;
        private int connectionAttempts = 0;
        private static byte[] _buffer = new byte[1024];
        private Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        private Socket _broadcastSocket;
        private EndPoint EP;

        public ClientConnection(DataManager dataManager) 
        {
            this.dataManager = dataManager;
            this.serverPort = 250;
            this.serverIP = "";
            this.serverIPNumber = 0;
            this.connectionAttempts = 0;
            this.isConnected = false;
        }

        public void SetServerPort(int serverPort) 
        {
            this.serverPort = serverPort;
        }

        public void SetServerIP(string serverIP) 
        {
            this.serverIP = serverIP;
        }

        public bool GetIsConnected() 
        {
            return this.isConnected;
        }

        public void Send(string text)
        {
            byte[] data = Encoding.ASCII.GetBytes(text);
            try
            {
                _clientSocket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), _clientSocket);
            }
            catch (SocketException) 
            {
                this.isConnected = false;
                System.Console.WriteLine("Server not connected!");
                return;
            }
        }

        public void ConnectToServer() 
        {
            int recv = 0;
            this.connectionAttempts = 0;
            this._broadcastSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

            while (recv <= 0)
            {
                IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9050);
                this.EP = (EndPoint)endPoint;
                this._broadcastSocket.Bind(endPoint);

                byte[] byteIP = new byte[1024];

                recv = _broadcastSocket.ReceiveFrom(byteIP, ref EP);
                this.serverIP = Encoding.ASCII.GetString(byteIP);

                if (recv > 0) 
                {
                    _broadcastSocket.Close();
                }
                else if (this.connectionAttempts > 5)
                {
                    return;
                }
                else 
                {
                    this.connectionAttempts++;
                }
            }

            this.serverIP = this.serverIP.Replace("\0", string.Empty);

            try
            {
                _clientSocket.BeginConnect(this.serverIP, this.serverPort, new AsyncCallback(ConnectCallback), this._clientSocket);
            }
            catch (SocketException) 
            {
                return;
            }
        }
        #region Socket events
        private void ConnectCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;

            if (_clientSocket.Connected)
            {
                socket.EndConnect(ar);
                System.Console.WriteLine("Connected!");
                this.isConnected = true;
                this.serverIP = socket.RemoteEndPoint.ToString().Split(':')[0];
                socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
            }
            else
            {
                System.Console.WriteLine("Can't connect to server");
                this.isConnected = false;
            }
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;

            int received = 0;
            try
            {
                received = socket.EndReceive(ar);
            }
            catch 
            {
                System.Console.WriteLine("Server disconnected!");
                this.isConnected = false;
                this._clientSocket.Disconnect(true);
                return;
            }

            if (received > 0)
            {
                byte[] dataBuff = new byte[received];
                Array.Copy(_buffer, dataBuff, received);

                string text = Encoding.ASCII.GetString(dataBuff);

                System.Console.WriteLine("Client recieved: " + text);

                if (text.Contains("Dive:"))
                {
                    string[] textElement = text.Split(',');
                    dataManager.diveCode = textElement[0].Remove(0, 5);
                    textElement[1] = textElement[1].Remove(0, 3);
                    string[] elem = textElement[1].Split('/');
                    dataManager.diveOrder = (Int32.Parse(elem[0]) + 1).ToString() + '/' + elem[1];
                }
                else if (text.Contains("Name:"))
                {
                    dataManager.judgeName = text.Remove(0, 5);
                }
                System.Console.WriteLine("Client received: " + text);

                this.dataManager.isReceivedNewData = true;

                socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
            }
        }

        private void SendCallback(IAsyncResult ar)
        {
            Socket socket = (Socket)ar.AsyncState;
            socket.EndSend(ar);
        }
        #endregion
        #region private methods
        #endregion
    }
}
